package coupon

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/resultx"
	enum "mall/service/backend/common/enum/promotion"
	"mall/service/backend/common/utils/pointy"
	"mall/service/promotion/model"
	"mall/service/promotion/rpc/internal/svc"
	"mall/service/promotion/rpc/internal/utils/errorhandler"
	"mall/service/promotion/rpc/types/promotion"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetUserCouponListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetUserCouponListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserCouponListLogic {
	return &GetUserCouponListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetUserCouponListLogic) GetUserCouponList(in *promotion.BaseListReq) (*promotion.CouponListResp, error) {
	// logrus.Info(fmt.Sprintf("GetUserCouponList BaseListReq: %+v", in))
	// Preload:"coupon:Id,ActivityId,TemplateId,UserId,StartedAt,EndedAt;couponTemplate:Id,Name,Desc,Members,Mutex,Scope,Mode,Threshhold,DiscountMode,DiscountValue"  EagerLoad:"coupon:couponTemplate"  Filter:"Status,equalTo:10;UserId,equalTo:10001"  VisitorId:10001  PlatformId:3

	if in.VisitorId == nil || *in.VisitorId == 0 {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)
	// logrus.Info(fmt.Sprintf("GetCouponTemlateList preloadMap: %+v", preloadMap))
	// map[coupon:[Id ActivityId TemplateId UserId StartedAt EndedAt] couponTemplate:[Id Name Desc Members Mutex Scope Mode Threshhold DiscountMode DiscountValue]]
	// logrus.Info(fmt.Sprintf("GetCouponTemlateList eagerLoadMap: %+v", eagerLoadMap))
	// map[coupon:[couponTemplate]]

	couponEntityName := model.RpcEntityNames.PromotionCoupon
	_, hasSettingMap := model.RpcEntityPreloadMap[couponEntityName]
	if !hasSettingMap {
		return &promotion.CouponListResp{}, nil
	}

	couponRepositoryName := model.RepositoryNames.PromotionCoupon
	columnMap, hasColumnMap := model.RepositoryPreloadMap[couponRepositoryName]
	if !hasColumnMap {
		return &promotion.CouponListResp{}, nil
	}

	var selectCols []string
	if preloadFields, hasPreloadFields := preloadMap[couponEntityName]; hasPreloadFields {
		selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
	}

	filterMap := modelx.ParseFilter(in.Filter, columnMap)
	// logrus.Info(fmt.Sprintf("GetCouponTemlateList filterMap: %+v", filterMap))
	// map[status:map[equalTo:[10]] user_id:map[equalTo:[10001]]]
	if filterMap == nil {
		filterMap = make(map[string]map[string][]any)
	}
	filterMap[model.PromotionCouponColumns.UserID] = map[string][]any{
		"equalTo": {*in.VisitorId},
	}

	orderBy := modelx.ParseSorter(in.Sorter, columnMap)
	logrus.Info(fmt.Sprintf("GetUserCouponList orderBy: %s", orderBy))

	res, count, err := l.svcCtx.Coupon.GetList(l.ctx, model.M{
		"offset":  in.Offset,
		"limit":   in.Limit,
		"orderBy": orderBy,
		"select":  strings.Join(selectCols, ","),
	}, 2, filterMap)
	if err != nil {
		return nil, err
	}

	hasNextPage, hasPreviousPage := modelx.ParseCount(count)
	resp := &promotion.CouponListResp{
		HasNextPage:     &hasNextPage,
		HasPreviousPage: &hasPreviousPage,
	}

	if len(res) > 0 {
		var tids []any
		tidMap := make(map[uint64]uint64)
		for _, v := range res {
			if _, ok := tidMap[v.TemplateID]; !ok {
				tids = append(tids, v.TemplateID)
				tidMap[v.TemplateID] = v.CouponID
			}
		}

		subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
		if eagerLoadEntities, hasEagerLoadEntities := eagerLoadMap[couponEntityName]; hasEagerLoadEntities {
			for _, entity := range eagerLoadEntities {
				if entitySettingMap, hasEntitySettingMap := model.RpcEntityPreloadMap[entity]; hasEntitySettingMap {
					subEntitySettingMap[entity] = entitySettingMap
				}
			}
		}

		idMediaMap := make(map[uint64][]*promotion.MediaInfo)
		promotionMediaEntityName := model.RpcEntityNames.PromotionMedia
		if _, ok := subEntitySettingMap[promotionMediaEntityName]; ok {
			promotionMediaRepositoryName := model.RepositoryNames.PromotionMedia
			subPreloadFields, hasSubPreloadFields := preloadMap[promotionMediaEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[promotionMediaRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.PromotionAttachmentRelationshipColumns.ObjectID] = map[string][]any{
					"in": tids,
				}
				subFilterMap[model.PromotionAttachmentRelationshipColumns.ObjectType] = map[string][]any{
					"equalTo": {globalkey.AttachmentLogTypePromotionCoupon},
				}
				subSelectCols = append(subSelectCols, model.PromotionAttachmentRelationshipColumns.ObjectID)
				var orderBy = model.PromotionAttachmentRelationshipColumns.AttachmentThumbnail + " DESC," + model.PromotionAttachmentRelationshipColumns.AttachmentOrder
				list, _, err := l.svcCtx.CouponTemplate.GetMedia(l.ctx, model.M{
					"orderBy": orderBy,
					"select":  strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				if len(list) > 0 {
					for _, v := range list {
						idMediaMap[v.ObjectID] = append(idMediaMap[v.ObjectID], &promotion.MediaInfo{
							Uid:       &v.AttachmentRelationshipID,
							Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
							Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
							Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
							Uri:       &v.AttachmentSource,
							Url:       &v.AttachmentImageURL,
							AltText:   &v.AttachmentImageAlt,
							MetaData:  &v.AttachmentImageMeta,
						})
					}
				}
			}
		}

		idTemplateMap := make(map[uint64]*promotion.CouponTemlateInfo)
		couponTemplateEntityName := model.RpcEntityNames.PromotionCouponTemplate
		couponTemplateRepositoryName := model.RepositoryNames.PromotionCouponTemplate
		if _, hasEntitySettingMap := subEntitySettingMap[couponTemplateEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[couponTemplateEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[couponTemplateRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.PromotionCouponTemplateColumns.ID] = map[string][]any{
					"in": tids,
				}
				list, _, err := l.svcCtx.CouponTemplate.GetList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						var media []*promotion.MediaInfo
						if ms, ok := idMediaMap[v.ID]; ok {
							media = ms[:]
						}

						var threshhold string
						switch v.Mode {
						case enum.PromotionCouponModeAmount:
							threshhold = moneyx.FormatAmount(int64(v.Threshhold))
						case enum.PromotionCouponModeNumber:
							threshhold = strconv.FormatInt(int64(v.Threshhold), 10)
						}
						var discountValue string
						var regions *string
						switch v.DiscountMode {
						case enum.PromotionCouponRuleAmount:
							discountValue = moneyx.FormatAmount(int64(v.DiscountValue))
						case enum.PromotionCouponRuleDiscount:
							discountValue = strconv.FormatInt(int64(v.DiscountValue), 10)
						case enum.PromotionCouponRuleDeduction:
							discountValue = strconv.FormatInt(int64(v.DiscountValue), 10)
						case enum.PromotionCouponRuleOrderFreeShipping, enum.PromotionCouponRuleProductFreeShipping:
							if v.Regions.Valid {
								regions = pointy.GetPointer(string(v.Regions.JSON))
							}
						}
						price := strconv.FormatUint(v.GemPrice, 10)
						idTemplateMap[v.ID] = &promotion.CouponTemlateInfo{
							Id:                &v.ID,
							Name:              &v.Name,
							Desc:              &v.Description,
							FilteredDesc:      &v.DescriptionFiltered,
							Condition:         pointy.GetPointer(uint32(v.Mode)),
							ConditionValue:    pointy.GetPointer(threshhold),
							DiscountMode:      pointy.GetPointer(uint32(v.DiscountMode)),
							DiscountValue:     pointy.GetPointer(discountValue),
							ScopeType:         pointy.GetPointer(uint32(v.ScopeType)),
							GemPrice:          pointy.GetPointer(price),
							ExpireMode:        pointy.GetPointer(uint32(v.ExpireMode)),
							ExpireValue:       pointy.GetPointer(uint32(v.ExpireValue)),
							ApplyStartedAt:    pointy.GetPointer(modelx.FormatNullDotTime(v.ApplyStartedAt, "")),
							ApplyEndedAt:      pointy.GetPointer(modelx.FormatNullDotTime(v.ApplyEndedAt, "")),
							StartedAt:         pointy.GetPointer(modelx.FormatNullDotTime(v.StartedAt, "")),
							EndedAt:           pointy.GetPointer(modelx.FormatNullDotTime(v.EndedAt, "")),
							Sort:              pointy.GetPointer(uint32(v.Sort)),
							Status:            pointy.GetPointer(uint32(v.Status)),
							MemberRestriction: &v.MemberRestriction,
							CouponType:        pointy.GetPointer(uint32(v.CouponType)),
							MutexType:         pointy.GetPointer(uint32(v.MutexType)),
							Count:             pointy.GetPointer(uint32(v.Count)),
							PerLimit:          pointy.GetPointer(uint32(v.PerLimit)),
							Step:              pointy.GetPointer(uint32(v.CreateStep)),
							CreatedAt:         pointy.GetPointer(modelx.FormatTime(v.CreatedAt, "")),
							UpdatedAt:         pointy.GetPointer(modelx.FormatTime(v.UpdatedAt, "")),
							DeletedAt:         pointy.GetPointer(modelx.FormatNullDotTime(v.DeletedAt, "")),
							Regions:           regions,
							Media:             media,
						}
					}
				}
			}
		}

		for _, v := range res {
			var rule *promotion.CouponTemlateInfo
			if template, ok := idTemplateMap[v.TemplateID]; ok {
				rule = template
			}

			resp.List = append(resp.List, &promotion.CouponInfo{
				Id:         &v.CouponID,
				ActivityId: &v.ActivityID,
				TemplateId: &v.TemplateID,
				Code:       &v.CouponCode,
				UserId:     &v.UserID,
				OrderId:    &v.OrderID,
				StartedAt:  pointy.GetPointer(modelx.FormatNullDotTime(v.StartedAt, "")),
				EndedAt:    pointy.GetPointer(modelx.FormatNullDotTime(v.EndedAt, "")),
				UsedAt:     pointy.GetPointer(modelx.FormatNullDotTime(v.UsedAt, "")),
				Status:     pointy.GetPointer(uint32(v.Status)),
				Rule:       rule,
			})
		}
	}
	resp.Total = uint64(len(resp.List))

	return resp, nil
}
